﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Common.Utilities;
using PdfLib;

namespace Workflows.Components.TextMining.EnumerationObjects
{
    /// <summary>
    /// 
    /// </summary>
    public class FileTypeDetector
    {
        #region file type
        /// <summary>
        /// 
        /// </summary>
        /// <param name="rule"></param>
        /// <param name="filePath"></param>
        /// <param name="isEesnap"></param>
        /// <param name="isPhs2590"></param>
        /// <returns></returns>
        public List<int> DetermineFileTypes(ExtractionRule rule, string filePath, ref bool isEesnap, ref bool isPhs2590)
        {
            List<int> pageNumbers = new List<int>();

            PdfFileTypeDetectionFact fact = new PdfFileTypeDetectionFact();
            fact.FilePath = filePath;
            try
            {
                fact.PagesContainsPersonTable = this.FindPagesThatContainsTable(filePath, rule.TableHeader,
                                                                                rule.TableFooter);
                if (fact.PagesContainsPersonTable != null && fact.PagesContainsPersonTable.Count > 0)
                {
                    List<int> colHeaderOpApplyOrders = new List<int>();
                    foreach (int applyOrder in rule.ColHeaderOptions.Keys)
                    {
                        colHeaderOpApplyOrders.Add(applyOrder);
                    }
                    colHeaderOpApplyOrders.Sort();

                    fact.ColHeaderMatchScoresForPages = new Dictionary<int, int>();
                    fact.HasEmbeddedImageForPages = new Dictionary<int, bool>();
                    fact.HasEsnapTagForPages = new Dictionary<int, bool>();

                    foreach (int page in fact.PagesContainsPersonTable)
                    {
                        int orderChosen = colHeaderOpApplyOrders[0];
                        int score = this.GetBestColHeaderMatchScoreForPage(filePath, page, rule.ColHeaderOptions,
                                                                           ref orderChosen);
                        fact.ColHeaderMatchScoresForPages.Add(page, score);
                        List<PdfTableColumnHeaderMapping> colMappingOp = rule.ColHeaderOptions[orderChosen];
                        string concatedColHeader = string.Empty;
                        foreach (PdfTableColumnHeaderMapping colHeaderMapping in colMappingOp)
                        {
                            concatedColHeader += colHeaderMapping.FieldNameInPdf;
                        }
                        if (score > concatedColHeader.Length * ExtractionRule.MIN_COLHEADER_MATCH_SCORE)
                        {
                            bool hasImage = this.ContainsEmbeddedImageForPage(filePath, page);
                            if (hasImage)
                            {
                                fact.HasEmbeddedImageForPages.Add(page, true);
                            }
                            else
                            {
                                bool hasEsnapTag =
                                    this.ContainsEsnapTagForPage(filePath, page, ExtractionRule.ESNAP_TAG);
                                if (hasEsnapTag)
                                {
                                    fact.HasEsnapTagForPages.Add(page, true);
                                }
                            }
                        }
                    }
                }

                if (fact.PagesContainsPersonTable != null && fact.PagesContainsPersonTable.Count > 0)
                {
                    if (fact.HasEsnapTagForPages != null && fact.HasEsnapTagForPages.Count > 0)
                    {
                        isEesnap = true;
                    }
                    else if (fact.HasEmbeddedImageForPages != null && fact.HasEmbeddedImageForPages.Count > 0)
                    {
                        isPhs2590 = true;
                    }
                }
                pageNumbers = fact.PagesContainsPersonTable;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.ToString());
            }
            return pageNumbers;
        }

        private List<int> FindPagesThatContainsTable(string filePath, string tblHeader, string tblFooter)
        {
            int totalPage = PdfPropertyReader.GetPageCount(filePath);
            List<int> pageNumbers = new List<int>();
            try
            {
                PdfPosition headerPos =
                    PdfDocumentReader.SeekPattern(filePath, tblHeader);
                int pageRead = headerPos.PageNumber;
                while (pageRead < totalPage)
                {
                    if (!string.IsNullOrEmpty(tblFooter))
                    {
                        try
                        {
                            PdfPosition footerPos =
                                PdfDocumentReader.SeekPatternAfterPage(filePath, tblFooter, headerPos.PageNumber);
                            if (footerPos.PageNumber == headerPos.PageNumber)
                            {
                                pageNumbers.Add(headerPos.PageNumber);
                            }
                        }
                        catch
                        {
                            pageRead = totalPage + 1;
                        }
                    }
                    else
                    {
                        pageNumbers.Add(headerPos.PageNumber);
                    }

                    // read forward
                    try
                    {
                        headerPos.PageNumber = headerPos.PageNumber + 1;
                        headerPos =
                            PdfDocumentReader.SeekPatternAfterPage(filePath, tblHeader, headerPos.PageNumber);
                        pageRead = headerPos.PageNumber;
                    }
                    catch
                    {
                        pageRead = totalPage + 1;
                    }
                }
                if (pageRead == totalPage)
                    pageNumbers.Add(pageRead);
            }
            catch (Exception pdfReadException)
            {
                // no need to throw from here
                Console.WriteLine(pdfReadException.Message);
            }
            return pageNumbers;
        }

        private int GetBestColHeaderMatchScoreForPage(string filePath, int pageNum, Dictionary<int, List<PdfTableColumnHeaderMapping>> colHeaderMappingOps, ref int colHeaderOpApplyOrder)
        {
            int bestScore = 0;
            string pageContent = PdfTextReader.ReadPageContent(filePath, pageNum);
            foreach (int order in colHeaderMappingOps.Keys)
            {
                List<string> colHeaders = new List<string>();
                List<PdfTableColumnHeaderMapping> colMappingOp = colHeaderMappingOps[order];
                foreach (PdfTableColumnHeaderMapping colMapping in colMappingOp)
                {
                    if (!string.IsNullOrEmpty(colMapping.FieldNameInPdf))
                    {
                        colHeaders.Add(colMapping.FieldNameInPdf);
                    }
                }
                int score = this.GetColHeaderMatchScoreForPage(pageContent, colHeaders);
                if (score > bestScore)
                {
                    bestScore = score;
                    colHeaderOpApplyOrder = order;
                }
            }
            return bestScore;
        }

        private int GetColHeaderMatchScoreForPage(string pageContent, List<string> colHeaders)
        {
            List<string> headers = new List<string>();
            foreach (string colHeader in colHeaders)
            {
                headers.Add(colHeader.ToLower());
            }
            StringBuilder headerBuffer = new StringBuilder();
            foreach (string colHeader in colHeaders)
            {
                headerBuffer.Append(colHeader + " ");
            }
            string concatedHeaderString = headerBuffer.ToString();
            int distance = StringUtil.LongestCommonSubsequence(pageContent.ToLower(), concatedHeaderString);
            return distance;
        }

        private bool ContainsEmbeddedImageForPage(string filePath, int pageNum)
        {
            return (PdfImageReader.GetPageImage(filePath, pageNum) != null);
        }

        private bool ContainsEsnapTagForPage(string filePath, int pageNum, string tag)
        {
            string pageContent = PdfTextReader.ReadPageContent(filePath, pageNum);
            if (pageContent.IndexOf(tag) >= 0)
                return true;
            else
                return false;
        }
        #endregion
    }
}
